From: Colin Walters Date: Thu, 26 Jun 2025 13:59:28 +0000 (-0400) Subject: status: Add --json output X-Git-Tag: archive/raspbian/2025.7-2+rpi1^2^2~6^2~4^2~15^2 X-Git-Url: https://dgit.raspbian.org/%22http:/www.example.com/cgi/%22https://%22%22/%22http:/www.example.com/cgi/%22https:/%22%22?a=commitdiff_plain;h=09b684bbe668b7c4481233ffed219aed2617ea7d;p=ostree.git status: Add --json output This is way long overdue, and will be useful for a variety of things but is especially motivated by soft reboot testing. Signed-off-by: Colin Walters --- diff --git a/ci/gh-install.sh b/ci/gh-install.sh index 2260883b..014799d1 100755 --- a/ci/gh-install.sh +++ b/ci/gh-install.sh @@ -101,6 +101,7 @@ case "$ID" in libsystemd-dev libtool libcap2-bin + jq procps python3 python3-yaml diff --git a/ci/installdeps.sh b/ci/installdeps.sh index bb8a93b4..a370f9c9 100755 --- a/ci/installdeps.sh +++ b/ci/installdeps.sh @@ -22,7 +22,7 @@ pkg_builddep ostree pkg_install composefs-devel pkg_install sudo which attr fuse strace \ libubsan libasan libtsan redhat-rpm-config \ - elfutils fsverity-utils + elfutils fsverity-utils jq if test -n "${CI_PKGS:-}"; then pkg_install ${CI_PKGS} fi diff --git a/man/ostree-admin-status.xml b/man/ostree-admin-status.xml index aefa14db..3a4877af 100644 --- a/man/ostree-admin-status.xml +++ b/man/ostree-admin-status.xml @@ -81,6 +81,14 @@ License along with this library. If not, see . + + + + + Output in JSON format. + + + diff --git a/src/ostree/ot-admin-builtin-status.c b/src/ostree/ot-admin-builtin-status.c index d05d9928..63e0f3f4 100644 --- a/src/ostree/ot-admin-builtin-status.c +++ b/src/ostree/ot-admin-builtin-status.c @@ -25,22 +25,26 @@ #include "ostree.h" #include "ot-admin-builtins.h" #include "ot-admin-functions.h" +#include "ul-jsonwrt.h" #include static gboolean opt_verify; static gboolean opt_skip_signatures; static gboolean opt_is_default; +static gboolean opt_json; static GOptionEntry options[] = { { "verify", 'V', 0, G_OPTION_ARG_NONE, &opt_verify, "Print the commit verification status", NULL }, + { "json", 'J', 0, G_OPTION_ARG_NONE, &opt_json, "Emit JSON", NULL }, { "skip-signatures", 'S', 0, G_OPTION_ARG_NONE, &opt_skip_signatures, "Skip signatures in output", NULL }, { "is-default", 'D', 0, G_OPTION_ARG_NONE, &opt_is_default, "Output \"default\" if booted into the default deployment, otherwise \"not-default\"", NULL }, { NULL } }; + static gboolean deployment_print_status (OstreeSysroot *sysroot, OstreeRepo *repo, OstreeDeployment *deployment, gboolean is_booted, gboolean is_pending, gboolean is_rollback, @@ -182,6 +186,45 @@ deployment_print_status (OstreeSysroot *sysroot, OstreeRepo *repo, OstreeDeploym return TRUE; } +static gboolean +deployment_write_json (OstreeSysroot *sysroot, OstreeRepo *repo, OstreeDeployment *deployment, + gboolean is_booted, gboolean is_pending, gboolean is_rollback, + struct ul_jsonwrt *jo, GCancellable *cancellable, GError **error) +{ + ul_jsonwrt_object_open (jo, NULL); + + const char *ref = ostree_deployment_get_csum (deployment); + ul_jsonwrt_value_s (jo, "checksum", ref); + ul_jsonwrt_value_s (jo, "stateroot", ostree_deployment_get_osname (deployment)); + ul_jsonwrt_value_u64 (jo, "serial", ostree_deployment_get_deployserial (deployment)); + ul_jsonwrt_value_boolean (jo, "booted", is_booted); + ul_jsonwrt_value_boolean (jo, "pending", is_pending); + ul_jsonwrt_value_boolean (jo, "rollback", is_rollback); + ul_jsonwrt_value_boolean (jo, "finalization-locked", + ostree_deployment_is_finalization_locked (deployment)); + ul_jsonwrt_value_boolean (jo, "staged", ostree_deployment_is_staged (deployment)); + ul_jsonwrt_value_boolean (jo, "pinned", ostree_deployment_is_pinned (deployment)); + OstreeDeploymentUnlockedState unlocked = ostree_deployment_get_unlocked (deployment); + ul_jsonwrt_value_s (jo, "unlocked", ostree_deployment_unlocked_state_to_string (unlocked)); + + g_autoptr (GVariant) commit = NULL; + if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT, ref, &commit, error)) + return FALSE; + g_autoptr (GVariant) commit_metadata = g_variant_get_child_value (commit, 0); + const char *version = NULL; + const char *source_title = NULL; + (void)g_variant_lookup (commit_metadata, OSTREE_COMMIT_META_KEY_VERSION, "&s", &version); + (void)g_variant_lookup (commit_metadata, OSTREE_COMMIT_META_KEY_SOURCE_TITLE, "&s", + &source_title); + if (version) + ul_jsonwrt_value_s (jo, "version", version); + if (source_title) + ul_jsonwrt_value_s (jo, "source-title", source_title); + + ul_jsonwrt_object_close (jo); + return TRUE; +} + gboolean ot_admin_builtin_status (int argc, char **argv, OstreeCommandInvocation *invocation, GCancellable *cancellable, GError **error) @@ -206,6 +249,26 @@ ot_admin_builtin_status (int argc, char **argv, OstreeCommandInvocation *invocat if (booted_deployment) ostree_sysroot_query_deployments_for (sysroot, NULL, &pending_deployment, &rollback_deployment); + if (opt_json) + { + struct ul_jsonwrt jo_buf; + ul_jsonwrt_init (&jo_buf, stdout, 0); + struct ul_jsonwrt *jo = &jo_buf; + ul_jsonwrt_root_open (jo); + ul_jsonwrt_array_open (jo, "deployments"); + for (guint i = 0; i < deployments->len; i++) + { + OstreeDeployment *deployment = deployments->pdata[i]; + if (!deployment_write_json (sysroot, repo, deployment, deployment == booted_deployment, + deployment == pending_deployment, + deployment == rollback_deployment, jo, cancellable, error)) + return FALSE; + } + ul_jsonwrt_array_close (jo); + ul_jsonwrt_root_close (jo); + return TRUE; + } + if (opt_is_default) { if (deployments->len == 0) diff --git a/tests/admin-test.sh b/tests/admin-test.sh index 0c442cfd..079bd6cb 100644 --- a/tests/admin-test.sh +++ b/tests/admin-test.sh @@ -71,6 +71,12 @@ assert_not_file_has_content status.txt "pending" assert_not_file_has_content status.txt "rollback" validate_bootloader +# And verify our JSON export +${CMD_PREFIX} ostree admin status --json > status.json +jq -e '.deployments[0].pending == false' status.json +jq -e '.deployments[0].rollback == false' status.json +assert_streq "$(jq -r '.deployments[0].checksum' status.json)" "${rev}" + if has_ostree_feature composefs; then if ! test -f sysroot/ostree/deploy/testos/deploy/*.0/.ostree.cfs; then fatal "missing composefs"